import axios from 'axios'
import qs from 'qs'
import {ElNotification, ElMessageBox, ElMessage, ElLoading} from 'element-plus'
import router from '@/router'
import appConfig from '@/config'
import {removeAll, getToken} from '@/utils/local'

let loadingInstance = null
const cancelMap = new Map()
const errorCodes = [500, 502, 503, 504, 403, 404, 400, 401]
const errorCodeMap = {
  400: '请求错误',
  401: '登录状态失效，请重新登录',
  403: '拒绝访问',
  404: '请求地址不存在',
  500: '服务器繁忙',
  502: '网关错误',
  503: '服务不可用，服务器暂时过载或维护',
  504: '网关超时'
}
const loadingSvg = `
<path class="path" d="
  M 30 15
  L 28 17
  M 25.61 25.61
  A 15 15, 0, 0, 1, 15 30
  A 15 15, 0, 1, 1, 27.99 7.5
  L 15 15
" style="stroke-width: 4px; fill: rgba(0, 0, 0, 0)"/>
`

axios.defaults.withCredentials = true
axios.defaults.timeout = appConfig.TIMEOUT

// 请求拦截器
axios.interceptors.request.use(config => {
  const {
    lock = true,
    loading = true,
    text = '加载中...',
    spinner = loadingSvg,
    background = 'rgba(0, 0, 0, 0.7)',
  } = config
  // console.log(config)
  if (loading) {
    loadingInstance = ElLoading.service({
      lock,
      text,
      spinner,
      background
    })
  }
  removePendingRequest(config)
  addPendingRequest(config)
  // 如果使用token则使用下面代码
  let token = getToken()
  if (token) {
    console.log(appConfig.TOKEN_NAME)
    config.headers[appConfig.TOKEN_NAME] = token
  }
  if (!appConfig.REQUEST_CACHE && config.method === 'get') {
    config.params = config.params || {}
    // config.params['_'] = new Date().getTime()
  }
  Object.assign(config.headers, appConfig.HEADERS)
  return config
}, error => {
  return Promise.reject(error)
})

// 响应拦截器
axios.interceptors.response.use(response => {
  // console.log('response---', response)
  const {status, data} = response
  if ([401, 403].includes(status)) {
    removeAll()
    window.location.reload()
    return false
  }
  !data.flag && ElMessage.error(data.message)
  removePendingRequest(response.config)
  const timer = setTimeout(() => {
    loadingInstance && loadingInstance.close()
    clearTimeout(timer)
  }, appConfig.HIDE_LOADING_TIME)
  return response.data
}, error => {
  const timer = setTimeout(() => {
    loadingInstance && loadingInstance.close()
    clearTimeout(timer)
  }, appConfig.HIDE_LOADING_TIME)
  if (error.response) {
    const res = error.response
    const statusCode = res.status
    if (errorCodes.includes(statusCode)) {
      if ([401, 403].includes(statusCode)) {
        ElMessageBox.confirm('当前用户已被登出或无权限访问当前资源，请尝试重新登录后再操作。', '无权限访问', {
          type: 'error',
          center: true,
          showClose: false,
          showCancelButton: false,
          closeOnClickModal: false,
          confirmButtonText: '重新登录'
        }).then(() => {
          removeAll()
          router.replace({path: '/login'})
        }).catch(() => {})

        return
      } else {
        ElNotification.error({
          title: '请求错误',
          message: errorCodeMap[statusCode]
        })
      }
    } else {
      ElNotification.error({
        title: '请求错误',
        message: res.data.message || `Status:${res.status}，未知错误！`
      })
    }
  } else {
    console.error(error)
    ElNotification.error({
      title: '请求错误',
      message: "请求服务器无响应！"
    })
  }

  return Promise.reject(error.response)
})

// 根据请求的信息，生成请求 Key
function generateRequestKey(config) {
  const {url, params, data = {}} = config
  const method = config.method || 'get'
  return [method, url, qs.stringify(params), qs.stringify(data)].join('&')
}

// 添加请求
function addPendingRequest(config) {
  const requestKey = generateRequestKey(config)
  config.cancelToken =
    config.cancelToken ||
    new axios.CancelToken(cancel => {
      if (!cancelMap.has(requestKey)) {
        // 如果 pending 中不存在当前请求，则添加到cancelMap
        cancelMap.set(requestKey, cancel)
      }
    })
}

// 移除本次请求
function removePendingRequest(config) {
  const requestKey = generateRequestKey(config)
  // 检查是否存在重复请求，若存在则取消已发的请求
  if (cancelMap.has(requestKey)) {
    const cancel = cancelMap.get(requestKey)
    cancel(requestKey)
    cancelMap.delete(requestKey)
  }
}

// 清空 pending 中的请求（在路由跳转时调用）
export const clearRequestPending = () => {
  for (const [url, cancel] of cancelMap) {
    cancel(url)
  }
  cancelMap.clear()
}

export default axios